# -*- coding: utf-8 -*-
# SSHexec 检查文件

# 系统或第三方模块
import os
from src.utils import tlog, elog
from fabric import Connection
from typing import Dict, Any
from posixpath import join as posix_join
from invoke.exceptions import UnexpectedExit

# 自定义模块
import src.transfer.transfer_utils as transfer_utils

def check_path_exists(conn: Connection, path: str, use_sudo: bool) -> bool:
    """检查路径是否存在"""
    command_check_exists = f"test -e {path}"
    try:
        conn.sudo(command_check_exists, hide=True) if use_sudo else conn.run(command_check_exists, hide=True)
        elog.info(f"路径存在，当前路径: {path}")
        return True
    except UnexpectedExit:
        elog.warning(f"路径不存在，当前路径: {path}")
        return False

def check_disk_space(conn: Connection, current_path: str, original_size: int, use_sudo: bool) -> int:
    """
    功能：
        检查远程路径所在磁盘的剩余空间是否充足
    
    参数：
        conn: Connection对象 - SSH连接对象
        current_path: str - 要检查的路径
        original_size: int - 原始文件大小（字节）
        use_sudo: bool - 是否使用sudo权限执行命令
    
    返回：
        int - 剩余磁盘空间（KB）
    
    异常：
        抛出Exception当无法获取磁盘空间或空间不足时
    """
    # 构造命令获取指定路径的剩余空间(KB)
    df_command = f"df -Pk {current_path} | tail -n 1 | awk '{{print $4}}'"
    
    try:
        # 根据权限选择执行方式
        cmd_exec = conn.sudo if use_sudo else conn.run
        disk_free_result = cmd_exec(df_command, hide=True)
        elog.info(f"命令获取路径 {current_path} 的磁盘空间信息成功，命令返回结果: {disk_free_result.stdout.strip()}")

    except UnexpectedExit as e:
        elog.error(f"无法获取路径 {current_path} 的磁盘空间信息: {str(e)}")
        raise Exception(f"无法获取磁盘空间信息")
    
    try:
        disk_free = int(disk_free_result.stdout.strip())
    except ValueError as e:
        elog.error(f"磁盘空间信息转换失败: {disk_free_result.stdout.strip()}")
        
    # 计算所需最小空间(原始大小的2倍，转换为KB)
    required_space = original_size / 512  # 等价于 original_size*2/1024
    
    # 检查磁盘空间是否充足
    if disk_free != '':
        if disk_free < required_space:
            error_msg = f"路径 {current_path} 磁盘空间不足, 需要 {required_space}KB, 剩余 {disk_free}KB"
            elog.error(error_msg)
            raise Exception(error_msg)
        else:
            elog.info(f"路径 {current_path} 磁盘空间充足, 剩余空间: {transfer_utils.format_size(disk_free*1000)}")
    else:
        elog.warning(f"无法获取路径 {current_path} 的磁盘空间信息，跳过磁盘空间检查限制")
    
    return disk_free


def check_single_path_writable(conn: Connection, current_path: str, use_sudo: bool) -> None:
    """
    功能：
        检查单个路径是否可写
    
    参数：
        conn: Connection对象 - SSH连接对象
        current_path: str - 要检查的路径
        use_sudo: bool - 是否使用sudo权限执行命令

    返回：
        None
    """
    
    # 检查可写性（创建测试文件）
    import random
    import string
    random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=12))
    test_file = posix_join(current_path, f".write_test_{random_str}")
    
    # 检查路径是否可写
    try:
        if use_sudo:
            conn.sudo(f"touch {test_file}", hide=True, warn=True)
        else:
            conn.run(f"touch {test_file}", hide=True, warn=True)
        elog.info(f"路径可写，当前路径: {current_path}")
    except UnexpectedExit as e:
        elog.error(f"创建测试文件失败 {test_file} : {e}")
        raise Exception(f"创建测试文件失败")

    finally:
    # 清理测试文件
        try:
            if use_sudo:
                conn.sudo(f"rm -f {test_file}", hide=True, warn=True)
            else:
                conn.run(f"rm -f {test_file}", hide=True, warn=True)
            elog.info(f"测试文件已成功删除 {test_file}")
        except UnexpectedExit as e:
            elog.error(f"清理测试文件失败 {test_file}: {e}")
            raise Exception(f"清理测试文件失败")


def perform_pre_checks(conn: Connection, remote_path: str, original_size: int, use_sudo: bool, result: Dict[str, Any]) -> None:
    """
    功能：
        执行所有预检查，包括路径是否存在、磁盘空间是否充足、路径是否可写
    
    参数：
        conn: Connection对象 - SSH连接对象
        remote_path: str - 要检查的远程路径
        original_size: int - 原始文件大小（字节）
        use_sudo: bool - 是否使用sudo权限执行命令
        result: Dict[str, Any] - 存储检查结果的字典
    
    返回：
        None
    """

    current_path = remote_path.rstrip('/')
    
    while current_path:
        elog.debug(f"当前路径: {current_path}")
        # 检查当前路径是否存在
        if check_path_exists(conn, current_path, use_sudo):
            elog.debug(f"当前路径存在")
            # 检查远程路径磁盘空间
            result["disk_free"] = check_disk_space(conn, current_path, original_size, use_sudo)
            # 检查远程路径磁盘可写性
            check_single_path_writable(conn, current_path, use_sudo)
            elog.info(f"当前路径磁盘可写")
            return 
        current_path = os.path.dirname(current_path)
        elog.debug(f"返回上一级路径")
    elog.error(f"无法找到可写路径")
    raise Exception("无法找到可写路径")


def check_unix_os(conn: Connection) -> bool:
    """
    检查远程操作系统是否为Unix-like
    Args:
        conn: 已建立的连接
    Returns:
        bool: 是否为Unix-like操作系统
    """
    # 首选检查 uname
    try:
        os_result = conn.run("uname -s", hide=True)
        os_result_str = os_result.stdout.strip().lower() if os_result.stdout else ""
        elog.info(f"uname -s 返回信息：{os_result_str}")
        if 'linux' in os_result_str or 'bsd' in os_result_str:
            elog.info("Unix操作系统检查通过")
            return True
        else:
            elog.warning("Unix操作系统检查未通过，停止本次传输任务")
            raise Exception("非Unix系统")
    except UnexpectedExit as e:
        elog.info(f"uname -s 返回信息：{e}")
        elog.error("uname -s 命令执行异常")
        raise Exception(str(e))
